﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
//Test01.6.2 添加对 Models 的引用, 以便访问 Models 的类
using WebAppMVCTest01.Models;
//Test01.7.2 添加对 ViewModels 的引用
using WebAppMVCTest01.ViewModels;

#region -- 说明 --
/**********************************************************************************************************
*
* 创建 ViewResult 对象将会渲染成视图来给用户反馈
* * ViewResult
* * * 创建了 ViewPageActivator 对象
* * * 选择了正确的 ViewEngine , 并且会给 ViewEngine 的构造函数传 ViewPageActivator 对象的参数
* * ViewEngine
* * * 创建 View 类的对象
* * * 调用 View 的 RenderView 方法
*
* ViewResult 是 HTML 响应, 而 ContentResult 是标准的文本响应,仅返回字符串类型.
*
* ActionResult 、 ViewResult 以及 ContentResult 的关系
* * ActionResult
* * ┣ ContentResult
* * ┗ ViewResultBase
* * *  ┗ ViewResult
*
* 添加对 Models 的 using 引用后即可以使用 Models 的类
*
* ViewData、ViewBag 是 Controller 与 View 之间值传递的内容. 但实际使用中它们并不是最佳选择
* (强制转换类型导致效率低, 没有类型安全容易运行时出错, 数据的收发之间没有正确的连接)
* 于是需要 "强类型 View"
*
* 为了更符合 MVC 架构思想, 添加 ViewModel 作为 View 的数据容器, 用于维护 Model 与 View 之间的数据传递
* * Model     是 业务 相关数据, 是根据 业务和数据结构 创建的
* * ViewModel 是 视图 相关数据, 是根据      View      创建的
* * Controller 根据 Model 对象创建并初始化 ViewModel 对象
* * 应该为每个 View 创建其对应的 ViewModel , 这样会使项目具有更高的适应性
* 
* 使用 EntityFramework 的代码优先自动生成数据库与数据表
* * 以 DataAccessLayer (数据访问层) 里的类来负责完成数据访问的操作
* * Web.config 里 connectionStrings (连接字符串) 的 name 属性需要与数据访问层的类名称相同
* * 可以通过在数据访问层的类里定义构造函数来更改连接字符串的名称
* * * 如 public SalesERPDAL() : base("NewName"){ }
*
**********************************************************************************************************/
#endregion

namespace WebAppMVCTest01.Controllers
{
    public class Test01Controller : Controller
    {
        // GET: Test01

        #region Test01.1 返回 字符串类型 到 View
        //Test01.1.1 创建 GetString() 方法, 直接返回字符串
        public string GetString() => "Hello World!";

        //Test01.1.2 按 F5 运行调试, 浏览器输入 //localhost:xxx/Test01/getstring 得到本方法的输出结果
        //  url 中的 Test01 即为 Test01Controller 的名称
        //这里的 GetString() 方法被称为 Action 方法 ( Controller 内的 public 类型的方法)
        //在 ASP.NET MVC 中, 我们创建的 GetString() 轻松地返回了一个 string 类型的响应;
        //  而在 ASP.NET Web Forms 中默认的返回请求是 HTML 的, 如需返回其他类型的请求, 必须创建 HTTP 处理器
        #endregion


        #region Test01.2 返回 "复杂类型" 到 View
        //Test01.2.1 在本命名空间创建 Customer 类
        //Test01.2.2 创建 GetCustomer() 方法, 返回一个 Customer 类型的对象
        public Customer GetCustomer()
        {
            Customer c = new Customer
            {
                CustomerName = "Customer 1",
                Address = "Address 1"
            };
            return c;
        }
        //Test01.2.3 按 F5 运行调试, 浏览器输入 //localhost:xxx/Test01/getcustomer 得到本方法的输出结果
        //  返回类型为类似这样的对象时, 将调用此对象的 ToString() 方法, 得到的结果是 NameSpace.ClassName 形式的类名
        //Test01.2.4 为了得到该对象里的内容, 这里在 Customer 类中重写了 ToString() 方法
        #endregion


        #region Test01.3 定义非 Action 方法
        //使用 [NonAction] 属性修饰的方法, 虽然是 public 的, 但也不会被浏览器通过url访问到 (即不是 Action 方法)
        [NonAction]
        public string SimpleMethod() => "This is not a Action Method!";
        #endregion


        #region Test01.4 返回 ViewResult 对象到 指定的 View 页面
        //Test01.4.1 创建 GetView() 方法, 返回一个 ViewResult 对象
        //  此处 View("MyView01") 中的 MyView01 即需要返回的视图名称
        public ActionResult GetView() => View("MyView01");

        //Test01.4.2 右击此方法名选择添加 View (视图) 命名为 MyView01
        //  或者也可以直接在 Views 文件夹下的 Test01 (对应此 Controller 的名称) 文件夹里创建一个名为 MyView01.cshtml 的文件
        //Test01.4.3 按 F5 运行调试, 浏览器输入 //localhost:xxx/Test01/getview 得到本方法指向的 MyView01.cshtml 页面
        #endregion


        #region Test01.5 试验 View 与 Controller 间的松散关系
        //ASP.NET MVC 的 View 和 Controller 不是严格匹配的
        //一个 Controller Action Method 可以引用多个 View
        //一个 View 也可以被多个 Controller Action Method 引用
        //位于 Shared 文件夹的 View 可以被所有 Controller 引用
        //另外, 此处尝试以属性返回 ActionResult
        public ActionResult GetRandomNum() => RandomNum;
        public ActionResult RandomNum
        {
            get
            {
                Random rd = new Random();
                if (rd.Next(100) % 10 == 0)
                    return View("MyView01");
                else if (rd.Next() % 2 == 0)
                    return View("MyViewTest01");
                else
                    return View("MyViewTest02");
            }
        }
        #endregion


        #region Test01.6 从 Controller 传递数据到 指定的 View 页面
        //Test01.6-1 分别通过 ViewData 、 ViewBag 、 Model 实现 Controller 与 View 的数据传递
        //Test01.6-1.1 在 Models 文件夹创建 Employee 类 (Employee.cs)
        //Test01.6-1.2 添加对 Models 的 using 引用
        //Test01.6-1.3 创建 GetEmployee01() 方法
        public ActionResult GetEmployee01()
        {
            //Test01.6-1.4 实例化 Models.Employee 类, 赋值并创建到 ViewData 中, 命名为 vdEmployee , 在对应视图中将可以通过 ViewData 访问此内容
            Random rd = new Random();
            Employee emp = new Employee()
            {
                FirstName = "From",
                LastName = "ViewData",
                Salary = rd.Next()
            };
            ViewData["vdEmployee"] = emp;

            //Test01.6-1.5 修改 emp 的值, 并创建到 ViewBag 中, 命名为 vbEmployee , 在对应视图中将可以通过 ViewBag 访问此内容
            emp = new Employee()
            {
                //FirstName = "View",
                LastName = "ViewBag",
                Salary = rd.Next()
            };
            ViewBag.vbEmployee = emp;

            //Test01.6-1.6 修改 emp 的值, 并将此 Model 与 View 一起传递到页面, 在对应视图中将可以通过 View 的强类型访问此内容
            emp = new Employee()
            {
                //FirstName = "View",
                LastName = "Model",
                Salary = rd.Next()
            };
            //返回 View
            //  此时会连带 ViewData、ViewBag 的内容一同返回
            //  返回 View 时, 同时传递 Model 数据 ( emp ), 以便在 View 中使用强类型
            return View("MyViewEmp01", emp);//View("MyViewEmp01");
        }

        //Test01.6-2 通过 ViewModels 实现 Controller 与 View 的数据传递
        //Test01.6-2-1.1 新建文件夹 ViewModels 并在该文件夹内创建 EmployeeViewModel 类, 作用相当于前面的 Employee 类
        //Test01.6-2-1.2 添加对 ViewModels 名称空间的 using
        //Test01.6-2-1.3 创建 GetEmployee02() 方法, 将 EmployeeViewModel 类实例化、赋值并通过 View 传递 ViewModels 数据 ( vmEmp )
        public ActionResult GetEmployee02()
        {
            Employee emp = new Employee()
            {
                FirstName = "From",
                LastName = "ViewModel",
                Salary = 20000
            };
            EmployeeViewModel vmEmp = new EmployeeViewModel()
            {
                EmployeeName = $"{emp.FirstName} {emp.LastName}",
                Salary = emp.Salary.ToString("c"),
                SalaryColor = (emp.Salary > 15000) ? "yellow" : "green",
            };
            return View("MyViewEmp02", vmEmp);
        }

        //Test01.6-2-2.1 在 ViewModels 文件夹里创建 EmployeeListViewModel 类、 EmployeeBusinessLayer 类
        //Test01.6-2-2.2 创建 GetEmployee03() 方法, 传递 ViewModels 数据到指定 View
        public ActionResult GetEmployee03()
        {
            EmployeeListViewModel empListViewModel = new EmployeeListViewModel();

            List<EmployeeViewModel> empViewModels = new List<EmployeeViewModel>();

            EmployeeBusinessLayer empBusLayer = new EmployeeBusinessLayer();
            List<Employee> employees = empBusLayer.GetEmployees();

            foreach (Employee e in employees)
            {
                EmployeeViewModel empViewModel = new EmployeeViewModel()
                {
                    EmployeeName = $"{e.FirstName} {e.LastName}",
                    Salary = e.Salary.ToString("c"),
                    SalaryColor = (e.Salary > 15000) ? "yellow" : "green",
                };
                empViewModels.Add(empViewModel);
            }

            empListViewModel.Employees = empViewModels;

            return View("MyViewEmp03", empListViewModel);
        }
        #endregion


        #region Test01.7 通过 Entity Framework 代码优先 自动创建数据库及数据表, 并完成数据查询
        //Test01.7.1 将 Models.Employee 类复制粘贴成一个 Models.EFEmployee 类, 添加主键标识, EF 创建数据表的字段时将以这个类的字段作为依据 (这里是为了跟前面的试验区分开来)
        //Test01.7.2 从 ViewModels.EmployeeViewModel 派生一个 EFEmployeeViewModel 类 (为了做区分, 相当于是复制了一个不同名字的类)
        //Test01.7.3 在 ViewModels.EmployeeListViewModel 中添加一个 List<EFEmployeeViewModel> EFEmployees 成员 (同样是为了区分, 本质上与 List<EmployeeViewModel> Employees 一样)
        //Test01.7.4 通过 NuGet 添加 EntityFramework 引用
        //Test01.7.5 Web.config 中添加 connectionStrings 标记, 写好数据库连接设置字符串, connectionStrings 的 name 属性要与数据访问层类名相同, 以此为数据访问层与数据库之间映射的依据
        //Test01.7.6 创建数据访问层: 新建文件夹 DataAccessLayer 并在其中创建 SalesEPRDAL 类
        //Test01.7.7 在 ViewModels.EmployeeBusinessLayer 中添加 GetEFEmployees() 方法, 调用数据访问层, 获取需要的数据
        //Test01.7.8 Controller 中添加名为 GetEFEmployee() 的 Action 方法
        public ActionResult GetEFEmployee()
        {
            EmployeeListViewModel empListViewModel = new EmployeeListViewModel();

            List<EFEmployeeViewModel> efEmpViewModels = new List<EFEmployeeViewModel>();

            EmployeeBusinessLayer empBusLayer = new EmployeeBusinessLayer();
            List<EFEmployee> efEmployees = empBusLayer.GetEFEmployees();

            foreach (EFEmployee e in efEmployees)
            {
                EFEmployeeViewModel efEmpViewModel = new EFEmployeeViewModel()
                {
                    EmployeeName = $"{e.FirstName} {e.LastName}",
                    Salary = e.Salary.ToString("c"),
                    SalaryColor = (e.Salary > 15000) ? "yellow" : "green",
                };
                efEmpViewModels.Add(efEmpViewModel);
            }

            empListViewModel.EFEmployees = efEmpViewModels;

            return View("MyViewEmp04", empListViewModel);
        }
        //运行过程:
        //按 F5 运行调试, 浏览器输入 //localhost:xxx/Test01/GetEFEmployee 服务器将先运行 Controller 的 GetEFEmployee() 方法,
        //执行到 EmployeeBusinessLayer 的 GetEFEmployees() 方法时, 调用了数据访问层,
        //因为是首次运行, EF将依据 Web.config 文件中的 connectionStrings 标记内容创建或连接指定数据库 (这里是在本项目目录创建一个 App-Data 文件夹并创建数据库文件 WebAppMVCTest01.DataAccessLayer.SalesEPRDAL )
        //创建并连接数据库后, EF再根据 connectionStrings 的 name 属性找到同名的数据访问层, 按数据访问层的定义来创建数据表, 包括表名与列名
        //创建数据表完成后, 才真正执行 EmployeeBusinessLayer 的 GetEFEmployees() 方法, 从该表中返回所有行 (当然, 此时该表为空)
        //Controller 获得返回数据后赋值到 EmployeeListViewModel 的实例中, 返回给指定 View 页面, 最后 View 将结果呈现
        //退出调试, 通过 视图\其他窗口\SQL Server 对象资源管理器 可以查看到新增的数据库 (localdb)\MSSQLLocalDB, 展开可以看到有两张表 dbo.__MigrationHistory 和 dbo.TblEFEmployee
        //右击 dbo.TblEFEmployee 选择查看数据, 此时可以在表中键入数据, 完成后再次按 F5 运行调试, 从 ../Test01/GetEFEmployee 中便可得到刚刚键入的数据

        //Test01.7-2 对 Test01.7 做些更改, 以使逻辑更为明确
        //  在 Views 文件夹下新建 Employee 文件夹
        //  MyViewEmp04.cshtml 复制到 Views\Employee 文件夹下, 重命名为 Index
        //  GetEFEmployee() Action 方法提取到 EmployeeController.Index()
        //  如此一来, URL就成了 //localhost:xxx/Employee/Index
        #endregion
    }


    #region Test01.2.1 创建 Customer 类
    /// <summary> Test01.2 自定义的类 </summary>
    public class Customer
    {
        public string CustomerName { get; set; }
        public string Address { get; set; }

        //如果不重写 ToString() 方法, 则输出结果为 NameSpace.ClassName 形式的类名
        public override string ToString() => $"{CustomerName}|{Address}";
    } 
    #endregion
}